package net.Group.MembershipService;

import net.jxta.credential.AuthenticationCredential;
import net.jxta.credential.Credential;
import net.jxta.document.Advertisement;
import net.jxta.document.Element;
import net.jxta.document.StructuredDocument;
import net.jxta.exception.PeerGroupException;
import net.jxta.exception.ProtocolNotSupportedException;
import net.jxta.id.ID;
import net.jxta.peer.PeerID;
import net.jxta.peergroup.PeerGroup;
import net.jxta.platform.ModuleSpecID;
import net.jxta.protocol.ModuleImplAdvertisement;
import net.jxta.membership.Authenticator;
import net.jxta.membership.MembershipService;
import net.jxta.service.Service;

import jxta.security.cipher.Cipher;
import jxta.security.crypto.JxtaCrypto;
import jxta.security.exceptions.CryptoException;
import jxta.security.impl.cipher.KeyBuilder;
import jxta.security.impl.cipher.SecretKey;
import jxta.security.impl.crypto.JxtaCryptoSuite;
import jxta.security.impl.publickey.RSA;
import jxta.security.impl.publickey.RSAKey;

import java.beans.PropertyChangeListener;
import java.net.URI;
import java.util.Vector;
import java.util.Enumeration;

/*
 * A Membership Service designed to demonstrate the use of the JXTA Crypto APIs
 */

public class SecureMembershipService implements MembershipService{

    private static final org.apache.log4j.Category LOG = 
        org.apache.log4j.Category.getInstance(SecureMembershipService.class.getName());
    private PeerGroup peerGroup;
    private ModuleImplAdvertisement implAdv;
    private ID assignedID;
    private Vector credentials               = new Vector();
    private Vector authenticationCredentials = new Vector();

    // This is the Public Key Algorithm that we will use in this example
    private RSA rsaAlgorithm; 
    private RSAKey rsaKey; 

    // store the creator id in here in order to contact later for access service
    //public PeerID creatorID;
    
    public static final ModuleSpecID SecureMembershipSpecID = (ModuleSpecID)
    ID.create(URI.create("urn:jxta:uuid-DeadBeefDeafBabaFeedBabe000000050406"));
	/*
	 * This is the constructor of the Membership Service
	 */
	public SecureMembershipService(){}
	
    /*
     * This method is called during an apply process.
     */
        
    public Authenticator apply( AuthenticationCredential unsubscribedCredential )
         throws PeerGroupException, ProtocolNotSupportedException{
    	     
		String sharedSecret = "Shared Secret Key";
        String method = unsubscribedCredential.getMethod();
        if( (null != method) && !"Apply".equals( method ) )
        	throw new ProtocolNotSupportedException(
            "Authentication method not recognized : Required \"Apply\" ");
		// First Extract the "Data" from the document
        StructuredDocument doc = 
            (StructuredDocument)unsubscribedCredential.getIdentityInfo();
        Enumeration enumm = doc.getChildren();
        Element element = (Element) enumm.nextElement();
        String encrypteddata =(String) element.getValue();

		// Now to decrypt this data, we must use the shared secret
		try{
        	// First build a key using the KeyBuilder , with RC4 as the algorithm
			SecretKey secretKey=(SecretKey)KeyBuilder.buildKey(KeyBuilder.TYPE_RC4,
														KeyBuilder.LENGTH_RC4,false);
			// All Keys need to have a minimum length for a successful encryption
			int minimumKeyLength = secretKey.getLength();
			// If our key does not fulfill the minimum length requirement,
			// we need to pad zero byte values with it and make it long enough
			byte[] keyArray;
			if(sharedSecret.length()<minimumKeyLength){
				// In this case we have to pad the key such that it has the 
				// minimum key length. The new byte[] ensures that all bytes are 
				// initialized to 0X00
				keyArray = new byte[minimumKeyLength];
				System.arraycopy(sharedSecret.getBytes(),0,keyArray,0,sharedSecret.length());
			} else {
				// The key is more that the required length. Hence no concerns 
				keyArray =sharedSecret.getBytes();
			}
			// Set the secret key with our value
			secretKey.setKey(keyArray, 0);
			// From the JXTACrypto Suite , get the RC4 cipher algorithm
			JxtaCrypto crypto = new JxtaCryptoSuite(JxtaCrypto.MEMBER_RC4,
													       null, (byte)0, (byte)0);		
			Cipher rc4Algorithm = crypto.getJxtaCipher();
		
		    //Initialize the Algorithm with the key and required mode
			rc4Algorithm.init(secretKey, Cipher.MODE_DECRYPT);
		    byte[] encrypteddataArray =encrypteddata.getBytes();
	        byte[] decryptedData = new byte[encrypteddataArray.length];// 
    		rc4Algorithm.doFinal(encrypteddataArray, 0, encrypteddataArray.length, decryptedData, 0);
    		String decryptedString = new String(decryptedData);
    		LOG.debug("DECRYPTED STRING IS =>"+ decryptedString);
    		System.out.println("DECRYPTED STRING IS =>"+ decryptedString);
    		if(!decryptedString.equals("Secret Data"))
    			throw new PeerGroupException("Data not properly encrypted !");
    		
		} catch(jxta.security.exceptions.CryptoException cryptoException){
			LOG.error("Error in Apply Process",cryptoException);
			throw new PeerGroupException("Failure in Apply"); 	
		}
        return new SecureAuthenticator(this,unsubscribedCredential);
    }         

    /*
     * This method is called when the peer is interested in joining the peer group.
     */
	     
    public Credential join(Authenticator authenticated )throws PeerGroupException{
		SecureAuthenticator secAuthenticator = (SecureAuthenticator)authenticated;
		if(!secAuthenticator.isReadyForJoin())
            throw new PeerGroupException("Authenticator rejected !!");            
		SecureCredential secureCredential= new SecureCredential(this);
		authenticationCredentials.addElement(authenticated.getAuthenticationCredential());
		credentials.addElement(secureCredential);
        return secureCredential;
    }

    /*
     * This method is called when the peer leaves the group.
     * Here we destroy all previous records of Authentication
     * Credentials and Credentials
     */
    public void resign() throws PeerGroupException{
    	//Destroy all previous records
    	authenticationCredentials = new Vector();
    	credentials               = new Vector();
	}

    /*
     * Returns all the Credentials for this peer 
     */
	public Enumeration getCurrentCredentials() throws PeerGroupException{
    	return credentials.elements();
    }

    /*
     * Returns all the Authentication Credentials for this peer 
     */
	public Enumeration getAuthCredentials()     throws PeerGroupException{
    	return authenticationCredentials.elements();
	}

    /*
     * Returns the ModuleImplAdvertisement
     */
    public Advertisement getImplAdvertisement(){
    	return implAdv;
    }

    /*
     * This method is called when the module is initialized.
     */
	public void init(PeerGroup peerGroup,
                     ID assignedID,Advertisement implAdv)
						throws PeerGroupException{

		this.peerGroup = peerGroup;
        this.assignedID=assignedID;
        this.implAdv=(ModuleImplAdvertisement)implAdv;
    }

    /*
     * Getter method for the peer group
     */
    public PeerGroup getPeerGroup(){
	    return peerGroup;
    }

    /*
     * Getter for the Interface.For the sake of simplicity ,
     * this object acts as it's own interface.
     */
    public Service getInterface() {
    	return this;
    }
    
    /*
     * Methods to be implemented by virtue of being a module.
     * Not used in this example 
     */
    
    public int startApp(String[] args){return 0;}

    public void stopApp(){}

    /*
     * This method is currently not supported .//??? Is this OK ?
     */
    public Credential makeCredential( Element element )
            throws PeerGroupException, Exception{
        return null;
    }

	@Override
	public Credential getDefaultCredential() throws PeerGroupException {
		// TODO Auto-generated method stub
		return null;
	}

	@Override
	public void addPropertyChangeListener(PropertyChangeListener listener) {
		// TODO Auto-generated method stub
		
	}

	@Override
	public void addPropertyChangeListener(String propertyName,
			PropertyChangeListener listener) {
		// TODO Auto-generated method stub
		
	}

	@Override
	public void removePropertyChangeListener(PropertyChangeListener listener) {
		// TODO Auto-generated method stub
		
	}

	@Override
	public void removePropertyChangeListener(String propertyName,
			PropertyChangeListener listener) {
		// TODO Auto-generated method stub
		
	}

}
